/*
 * This kernel module locates the sys_call_table by
 * kallsyms_lookup_name("sys_call_table"); debian 9 4.9.0-13-amd64 passed debian
 * 10 4.19.0-10 amd64 passed ubuntu 5.4.0-65 amd64 passed syscall proto
 * https://elixir.bootlin.com/linux/v5.10.27/source/include/linux/syscalls.h
 */

#include <asm/pgtable.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/utsname.h>

/*
** module macros
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("geeksword");
MODULE_DESCRIPTION("hook sys_execve");

unsigned long sys_call_table_addr = 0;
typedef asmlinkage long (*orig_execve)(const char __user* filename,
                                       const char __user* const __user* argv,
                                       const char __user* const __user* envp);
typedef asmlinkage long (*orig_getuid)(void);

orig_execve old_execve = NULL;
orig_getuid old_getuid = NULL;

// hooked mkdir function
asmlinkage long hooked_execve(const char __user* filename,
                              const char __user* const __user* argv,
                              const char __user* const __user* envp)
{
    printk("hooked_execve:%s\n", filename);
    // printk("%s%s---%s:%s\n", msg, filename, argv[0], envp[0]);
    return old_execve(filename, argv, envp);
}

asmlinkage long hooked_getuid(void)
{
    printk("hooked_getuid:%s\n", current->comm);
    return old_getuid();
}

/*******************************************************************
 * Name: 	obtain_sys_call_table_addr
 * Description:	Obtains the address of the `sys_call_table` in the system.
 *******************************************************************/
static int obtain_sys_call_table_addr(unsigned long* sys_call_table_addr)
{
    int           ret = 1;
    unsigned long temp_sys_call_table_addr;

    temp_sys_call_table_addr = kallsyms_lookup_name("sys_call_table");

    /* Return error if the symbol doesn't exist */
    if (0 == sys_call_table_addr) {
        ret = -1;
        goto cleanup;
    }

    printk("Found sys_call_table: %p", (void*)temp_sys_call_table_addr);
    *sys_call_table_addr = temp_sys_call_table_addr;

cleanup:
    return ret;
}

// memory protection shinanigans
unsigned int level;
pte_t*       pte;

// initialize the module
static int hooked_init(void)
{
    int ret;
    printk("+ Loading hook_mkdir module\n");
    ret = -1;
    ret = obtain_sys_call_table_addr(&sys_call_table_addr);
    if (ret != 1) {
        printk("- unable to locate sys_call_table\n");
        return 0;
    }

    // print out sys_call_table address
    printk("+ found sys_call_table at %08lx!\n", sys_call_table_addr);

    // now we can hook syscalls ...such as uname
    // first, save the old gate (fptr)
    old_execve = ((unsigned long*)(sys_call_table_addr))[__NR_execve];
    old_getuid = ((unsigned long*)(sys_call_table_addr))[__NR_getuid];
    // unprotect sys_call_table memory page
    pte = lookup_address((unsigned long)sys_call_table_addr, &level);

    // change PTE to allow writing
    set_pte_atomic(pte, pte_mkwrite(*pte));

    printk("+ unprotected kernel memory page containing sys_call_table\n");

    // now overwrite the __NR_uname entry with address to our uname
    ((unsigned long*)(sys_call_table_addr))[__NR_execve] =
        (unsigned long)hooked_execve;
    ((unsigned long*)(sys_call_table_addr))[__NR_getuid] =
        (unsigned long)hooked_getuid;
    printk("+ sys_execve hooked!\n");

    return 0;
}

static void hooked_exit(void)
{
    if (old_execve != NULL) {
        // restore sys_call_table to original state
        ((unsigned long*)(sys_call_table_addr))[__NR_execve] =
            (unsigned long)old_execve;
        ((unsigned long*)(sys_call_table_addr))[__NR_getuid] =
            (unsigned long)old_getuid;

        // reprotect page
        set_pte_atomic(pte, pte_clear_flags(*pte, _PAGE_RW));
    }

    printk("+ Unloading hook_execve module\n");
}

/*
** entry/exit macros
*/
module_init(hooked_init);
module_exit(hooked_exit);
